import SailGameObject from './SailGameObject';
import { ComponentAttrs } from './declare';


const DefaultBundle = {
    bundle: '',//
    info: {
        name: '',
        icon: '',
        desc: '',
        group: 'default',
    },
    create() {
        return null;
    },
    props() {
        return {};
    },
};

enum VarType {
    String = 'string',
    Boolean = 'boolean',
    Number = 'number',
}

const GameBehavior = 'behavior';
const GameObject = 'object';


class Sail {
    VarType: typeof VarType = VarType;
    
    public getComponentAttrs(comp): ComponentAttrs[] {
        let ret = {};
        // @ts-ignore
        const attrs = cc.Class.Attr.getClassAttrs(comp);
        Object.keys(attrs).forEach(key => {
            // @ts-ignore
            const values = key.split(cc.Class.Attr.DELIMETER);
            if (values.length === 2) {
                const [compProperty, prop] = values;
                if (!ret.hasOwnProperty(compProperty)) {
                    ret[compProperty] = {};
                }
                ret[compProperty][prop] = attrs[key];
            }
        });
        const result = [];
        Object.keys(ret).map((key: string) => {
            ret[key].name = key;
            result.push(ret[key]);
        });
        return result;
    }
    
    async loadGameBehavior(entry) {
        const { component } = entry;
        if (component && typeof component === 'function') {
        }
        return entry;
    }
    
    async loadGameObject(entry, bundleData: cc.AssetManager.Bundle) {
        entry = Object.assign({}, DefaultBundle, entry);
        return entry;
    }
    
    public allBundles = [];
    
    async loadBundle(name: string) {
        // todo bundleName需要支持http的方式
        const bundleData = await this.downloadBundle(name);
        //@ts-ignore
        const bundle = require(name);
        if (bundle) {
            let entry = bundle.default || bundle;
            if (typeof entry === 'object') {
                entry.bundle = bundleData.name; // 和bundle的名字保持一致
                const { type } = entry;
                let ret = null;
                if (type === GameBehavior) {
                    ret = this.loadGameBehavior(entry);
                } else if (type === GameObject) {
                    ret = this.loadGameObject(entry, bundleData);
                }
                if (ret) {
                    this.allBundles.push(ret);
                }
                return ret;
            }
        }
        return null;
    }
    
    async downloadBundle(name: string): Promise<cc.AssetManager.Bundle> {
        return new Promise((resolve, reject) => {
            cc.assetManager.loadBundle(name, (err, bundle) => {
                resolve(bundle);
            });
        });
    }
    
    async createGameObject(originObjectID: string, data: SailGameObject): Promise<cc.Node | null> {
        const gameObject = await this.loadBundle(data.bundle);
        if (gameObject && gameObject.create && typeof gameObject === 'object') {
            let node: cc.Node = await gameObject.create();
            if (node) {
                let objectScript = node.addComponent(SailGameObject);
                objectScript.originObjectID = originObjectID;
                objectScript.bundle = gameObject.bundle;
                if (data) {
                    this.setComponentProperties(objectScript, data);
                    await objectScript.init(data);
                }
                
                return node;
            }
        }
        return null;
    }
    
    public setComponentProperties(script, data: Record<string, any>) {
        if (data && typeof data === 'object' && !Array.isArray(data)) {
            const keys = Object.keys(data);
            keys.forEach(key => {
                if (this.hasProperty(script, key)) {
                    script[key] = data[key];
                } else {
                    console.log('key:', key);
                }
            });
        }
    }
    
    async serializeScene(node: cc.Node) {
        const ret = [];
        if (node) {
            for (let i = 0; i < node.children.length; i++) {
                const child = node.children[i];
                const script = child.getComponent(SailGameObject);
                if (script) {
                    let item = this._serializeComponent(child, script.constructor);
                    ret.push(item);
                    const dependencies = {};
                    // @ts-ignore
                    const allBundles = script.behaviors.concat(item.bundle);
                    // 行为
                    for (let i = 0; i < allBundles.length; i++) {
                        const bundle = allBundles[i];
                        const info = await this.loadBundle(bundle);
                        if (info && info.component) {
                            dependencies[bundle] = this._serializeComponent(child, info.component);
                        }
                    }
                    item['dependencies'] = dependencies;
                }
            }
        }
        return ret;
    }
    
    _serializeComponent(node, component) {
        let compData = {};
        const script = node.getComponent(component);
        if (script) {
            const attrs = this.getComponentAttrs(component);
            attrs.forEach(attr => {
                compData[attr.name] = script[attr.name];
            });
        }
        return compData;
    }
    
    hasProperty(obj, key: string) {
        let b = obj.hasOwnProperty(key);
        if (!b) {
            b = (key in obj);
        }
        if (!b) {
            let proto = obj;
            do {
                b = !!Object.getOwnPropertyDescriptor(proto, key);
                proto = Object.getPrototypeOf(proto);
            }
            while (!b && proto) ;
        }
        return b;
    }
}

const sail = new Sail();
(window as any).sail = sail;

export default sail;